// Copyright 2012 Jay Young. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview
 *
 * Test cases for various program components.
 */

var assert = require('assert'),
    TypeLinker = require('rdoc/typelinker').TypeLinker,
    Kind = require('rdoc/docmap').DocMap.Kind,
    commonTemplate = require('rdoc/templates/common'),

    types = {
      'relief.nav.Handler': {
        filename: '',
        kind: Kind.INTERFACE
      },
      'goog.disposable.IDisposable': {
        filename: '',
        kind: Kind.INTERFACE
      },
      'relief.auth.User': {
        filename: '',
        kind: Kind.INTERFACE
      }
    };

function getTypeLinker() {
  return new TypeLinker(types);
}


exports['test Primitive Type Parsing'] = function() {
  var linker = getTypeLinker();

  assert.equal(linker.processTypeExpr('boolean'), 'boolean');
  assert.equal(linker.processTypeExpr('string'), 'string');
  assert.equal(linker.processTypeExpr('number'), 'number');
  assert.equal(linker.processTypeExpr('null'), 'null');
  assert.equal(linker.processTypeExpr('undefined'), 'undefined');
  assert.equal(linker.processTypeExpr('<Any Type>'), '*');
};


exports['test Unregistered Symbol Parsing'] = {
  'test Simple Type Name': function() {
    var linker = getTypeLinker();
    
    assert.equal(linker.processTypeExpr('Date'), 'Date');
  },
  
  'test Full Qualified Type Name': function() {
    var linker = new TypeLinker(types);
    
    assert.equal(linker.processTypeExpr('goog.date.DateTime'),
                 'goog.date.DateTime');
  }
};


exports['test Registered Symbol Parsing'] = {
  'test Registered Type Gets Linked': function() {
    var linker = getTypeLinker(),
        type = 'relief.nav.Handler',
        expectedHref = 'relief_nav_handler.html';

    assert.equal(linker.processTypeExpr(type),
                  commonTemplate.typeLink({
                    type: type,
                    href: expectedHref,
                    kind: types[type].kind
                  }));
  }
};


exports['test Object Types w/ Value Types '] = {
  'test Object Type w/ boolean value parameter': function() {
    var linker = getTypeLinker(),
        primitiveValue = 'Object.<boolean>',
        primitiveExpected = 'Object.&lt;boolean&gt;',
        nestedParameterizedType = 'Object.<Object.<string, number>>',
        nestedExpected = 'Object.&lt;Object.&lt;string, number&gt;&gt;',
        unionValueType = 'Array.<(null|string)>',
        unionExpected = 'Array.&lt;?string&gt;';

    assert.equal(linker.processTypeExpr(primitiveValue), primitiveExpected);
    assert.equal(linker.processTypeExpr(nestedParameterizedType),
                 nestedExpected);
    assert.equal(linker.processTypeExpr(unionValueType), unionExpected);
  }
};


exports['test Object Types w/ Index & Value Types'] = {
  'test Object Type w/ Index and Value Types': function() {
    var linker = getTypeLinker(),
        type = 'Object.<string, number>',
        expected = 'Object.&lt;string, number&gt;';

    assert.equal(linker.processTypeExpr(type), expected);
  },
  
  'test Object Type w/ Union Value Types': function() {
    var linker = getTypeLinker(),
        type = 'Object.<relief.nav.Handler, (number|null)>',
        expected = 'Object.&lt;' + commonTemplate.typeLink({
          type: 'relief.nav.Handler',
          href: 'relief_nav_handler.html',
          kind: Kind.INTERFACE
        }) + ', ?number&gt;';

    assert.equal(linker.processTypeExpr(type), expected);
  }
};


exports['test Union types'] = {
  'test Simple Union Type': function() {
    var linker = getTypeLinker(),
        type = '(string|number)';

    assert.equal(linker.processUnionType(type), type);
  },
  
  'test Null Union Type': function() {
    var linker = getTypeLinker(),
        type = '(Object|null)';

    assert.equal(linker.processTypeExpr(type), '?Object');
  },
  
  'test Undefined Union Parameter': function() {
    var linker = getTypeLinker(),
        type = '(Object|undefined)';

    assert.equal(linker.processTypeExpr(type, true), 'Object=');
  },

  'test Undefined Union Type': function() {
    var linker = getTypeLinker(),
        type = '(Object|undefined)';

    assert.equal(linker.processTypeExpr(type), type);
  },

  'test Null, Optional Parameter': function() {
    var linker = getTypeLinker(),
        type = '(Date|null|undefined)';

    assert.equal(linker.processTypeExpr(type, true), '?Date=');
  },
  
  'test Null Undefined Union Type': function() {
    var linker = getTypeLinker(),
        type = '(Date|null|undefined)';

    assert.equal(linker.processTypeExpr(type), type);
  }
};


exports['test Function Types'] = function() {
  var linker = getTypeLinker(),
      type = 'function ((relief.auth.User|null)): ?',
      expected = 'function (?' + commonTemplate.typeLink({
        type: 'relief.auth.User',
        href: 'relief_auth_user.html',
        kind: Kind.INTERFACE
      }) + ') &#8658; ?';

  assert.equal(linker.processTypeExpr(type), expected);
};


exports['test Exceptions'] = {
  'test Unclosed Parameterized Object Type Throws': function() {
    var linker = getTypeLinker(),
        type = 'Object.<string';

    assert.throws(function() { linker.processTypeExpr(type)});
  },
  
  'test Unclosed Parameterized Array Type Throws': function() {
    var linker = getTypeLinker(),
        type = 'Array.<number';
    
    assert.throws(function() { linker.processTypeExpr(type)});
  },
  
  'test processParamterizedType Throws if Invalid': function() {
    var linker = getTypeLinker(),
        arrayWithIndex = 'Array.<number, number>',
        tooManyCommas = 'Object.<string, number, wtf>';

    assert.throws(function() { linker.processTypeExpr(arrayWithIndex)});
    assert.throws(function() { linker.processTypeExpr(tooManyCommas)});
  }
};


exports['test Really Complex Types'] = {
  'test Parse goog.Disposable.dependentDisposables_ type': function() {
    var linker = getTypeLinker(),
        type = '(Array.<(goog.disposable.IDisposable|null)>|null)',
        expected = '?Array.&lt;?' +
                    commonTemplate.typeLink({
                      type: 'goog.disposable.IDisposable',
                      href: 'goog_disposable_idisposable.html',
                      kind: Kind.INTERFACE
                    }) + '&gt;';

    assert.equal(linker.processTypeExpr(type), expected);
  }
};
